package com.zlc.workflow.core.process.cmd;

import com.zlc.workflow.constant.NodeType;
import com.zlc.workflow.core.instance.cmd.AbstractCmd;
import com.zlc.workflow.core.process.model.FlowNode;
import com.zlc.workflow.core.process.model.FlowPath;
import com.zlc.workflow.core.process.model.FlowProcess;
import com.zlc.workflow.flow.NodeModel;
import com.zlc.workflow.flow.NodeUtils;
import com.zlc.workflow.flow.node.Path;
import com.zlc.workflow.flow.node.ProcessModel;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
public class ParseXmlToJsonCmd extends AbstractCmd<FlowProcess> {

    private final String xmlContent;

    private final FlowProcess flowProcess;

    private final List<String> parseNodeIds;

    /**
     * 获取当前命令对象的所有带 @Autowired注解的属性，然后从spring上下文中获取到注入进去
     *
     */
    public ParseXmlToJsonCmd(Object params) {
        super(params);
        this.xmlContent = (String)params;
        this.flowProcess = new FlowProcess();
        this.parseNodeIds = new ArrayList<>();
    }

    @Override
    public FlowProcess execute() {
        ProcessModel processModel = NodeUtils.processModel(this.xmlContent,"");
        this.flowProcess.setProcessCode(processModel.getProcessCode());
        this.flowProcess.setProcessName(processModel.getProcessName());

        List<FlowNode> nodes = new ArrayList<>();
        NodeModel startNode = processModel.getStartNode();
        buildFlowNode(nodes,startNode);
        this.flowProcess.setNodes(nodes);
        return this.flowProcess;
    }


    private void buildFlowNode(List<FlowNode> nodes,NodeModel nodeModel){
        FlowNode node = initFlowNode(nodeModel);

        List<Path> paths = nodeModel.path();

        if(!(NodeType.JOIN.equals(nodeModel.nodeType()) || NodeType.END.equals(nodeModel.nodeType()))){
            nodes.add(node);
            parseNodeIds.add(nodeModel.nodeId());
            for(Path path : paths){
                FlowPath flowPath = buildPath(path);
                node.getPaths().add(flowPath);
                buildFlowNode(nodes,path.getNextNode());
            }
        }
        if((NodeType.JOIN.equals(nodeModel.nodeType()) || NodeType.END.equals(nodeModel.nodeType()))
                && joinNextCheck(nodeModel)){
            nodes.add(node);
            parseNodeIds.add(nodeModel.nodeId());
            for(Path path : paths){
                FlowPath flowPath = buildPath(path);
                node.getPaths().add(flowPath);
                buildFlowNode(nodes,path.getNextNode());
            }
        }
    }


    /**
     * 校验分支节点是否可以继续向后走
     */
    private boolean joinNextCheck(NodeModel nodeModel){
        List<NodeModel> nodeModels = nodeModel.preTask();
        List<String> preNodeIds = nodeModels.stream().map(NodeModel::nodeId).collect(Collectors.toList());
        return parseNodeIds.containsAll(preNodeIds);
    }


    private FlowNode initFlowNode(NodeModel nodeModel){
        FlowNode node = new FlowNode();
        node.setNodeId(nodeModel.nodeId());
        node.setNodeNum(nodeModel.nodeNum());
        node.setNodeName(nodeModel.nodeName());
        node.setX(nodeModel.x());
        node.setY(nodeModel.y());
        node.setNodeType(nodeModel.nodeType().value());
        node.setPaths(new ArrayList<>());
        return node;
    }

    private FlowPath buildPath(Path path){
        FlowPath node = new FlowPath();
        node.setNodeId(path.nodeId());
        node.setNodeNum(path.nodeNum());
        node.setNodeName(path.nodeName());
        node.setNodeType(NodeType.PATH.value());
        node.setTo(path.getTo());
        node.setTarget(path.getTarget());
        node.setSource(path.getSource());
        return node;
    }
}
